package com.siyu.predict.utils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.siyu.basedata.iothlpoint.domain.IotHLPoint;
import com.siyu.basedata.iothlpoint.service.IIotHLPointService;
import com.siyu.basedata.pptnrfuture.domain.PptnRFuture;
import com.siyu.basedata.pptnrfuture.service.IPptnRFutureService;
import com.siyu.basedata.pptnrlaste.domain.PptnRLaste;
import com.siyu.basedata.pptnrlaste.service.IPptnRLasteService;
import com.siyu.basedata.rsvrrlaste.domain.RsvrRLaste;
import com.siyu.basedata.rsvrrlaste.service.IRsvrRLasteService;
import com.siyu.basedata.wrprsrhych.domain.WrpRsrHych;
import com.siyu.basedata.wrprsrhych.service.IWrpRsrHychService;
import com.siyu.basedata.wrprsrwlstcparrl.domain.Wrprsrwlstcparrl;
import com.siyu.basedata.wrprsrwlstcparrl.service.IwrprsrwlstcparrlService;
import com.siyu.basedata.xajresult.domain.XajResult;
import com.siyu.basedata.zqrlb.domain.ZqrlB;
import com.siyu.basedata.zqrlb.service.IZqrlBService;
import com.siyu.model.checkmodel.CheckModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.*;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 获取站点数据序列
 */
@Component
public class DataUtil {
    @Autowired
    private IIotHLPointService iIotHLPointService;
    @Autowired
    private IPptnRLasteService pptnRLasteService;
    @Autowired
    private IwrprsrwlstcparrlService wrprsrwlstcparrlService;
    @Autowired
    private IRsvrRLasteService rsvrRLasteService;
    @Autowired
    private IPptnRFutureService pptnRFutureService;
    @Autowired
    private IZqrlBService zqrlBService;
    @Autowired
    private IWrpRsrHychService wrpRsrHychService;

    public static DataUtil drpDataUtil;

    @PostConstruct
    public void init() {
        drpDataUtil = this;
        drpDataUtil.iIotHLPointService = this.iIotHLPointService;
        drpDataUtil.pptnRLasteService = this.pptnRLasteService;
        drpDataUtil.wrprsrwlstcparrlService = this.wrprsrwlstcparrlService;
        drpDataUtil.rsvrRLasteService = this.rsvrRLasteService;
        drpDataUtil.zqrlBService = this.zqrlBService;
        drpDataUtil.wrpRsrHychService = this.wrpRsrHychService;
        drpDataUtil.pptnRFutureService = this.pptnRFutureService;

    }

    /**
     * 获取实测面雨量
     *
     * @param rscd
     * @param startTime
     * @param endTime
     * @param step
     * @return
     */
    public static HashMap<Object, Object> getAreaRainfall(String rscd, Date startTime, Date endTime, Integer step) throws Exception {
        List<String> rainStationCds = new ArrayList<>();
        List<Double> rainStationweight = new ArrayList<>();
        QueryWrapper<IotHLPoint> iotHLPointQueryWrapper = new QueryWrapper<>();
        iotHLPointQueryWrapper.eq("sttp", "3").eq("rscd", rscd);

        List<IotHLPoint> points = drpDataUtil.iIotHLPointService.list(iotHLPointQueryWrapper);
        for (int i = 0; i < points.size(); i++) {
            rainStationCds.add(points.get(i).getStcd());
            rainStationweight.add(points.get(i).getWeight());
        }
        // 时间序列
        List<String> tms = new ArrayList<>();
        // 面雨量数据
        List<Double> Pr = new ArrayList<>();
        for (int i = 0; i < rainStationCds.size(); i++) {
            int j = 0;
            // 查询雨量数据
            List<PptnRLaste> stationRains = drpDataUtil.pptnRLasteService.getRainfallData(DateUtil.format(startTime,"yyyy-MM-dd HH:mm:ss")
                    ,DateUtil.format(endTime,"yyyy-MM-dd HH:mm:ss"),step.toString(),rainStationCds.get(i));
            for (int i1 = 0; i1 < stationRains.size(); i1++) {
                Double precipitation = Double.valueOf(stationRains.get(i1).getDrp().toString()) * Double.valueOf(rainStationweight.get(i));
                if (i == 0) {
                    tms.add(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(stationRains.get(i1).getTm()));
                    Pr.add(precipitation);
                } else {
                    Pr.set(j, Pr.get(j) + precipitation);
                    j++;
                }
            }
        }
//        for (int i = 0; i < rainStationCds.size(); i++) {
//            int j = 0;
//            QueryWrapper<PptnRLaste> pptnRLasteQueryWrapper = new QueryWrapper<>();
//            pptnRLasteQueryWrapper.eq("stcd", rainStationCds.get(i))
//                    .ge(BeanUtil.isNotEmpty(startTime), "tm", startTime)
//                    .le(BeanUtil.isNotEmpty(endTime), "tm", endTime)
//                    .orderByAsc("tm");
//
//            List<PptnRLaste> stationRains = drpDataUtil.pptnRLasteService.list(pptnRLasteQueryWrapper);
//            for (int i1 = 0; i1 < stationRains.size(); ) {
//                Double precipitation = Double.valueOf(stationRains.get(i1).getDrp().toString()) * Double.valueOf(rainStationweight.get(i));
//                if (i == 0) {
//                    tms.add(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(stationRains.get(i1).getTm()));
//                    Pr.add(precipitation);
//                } else {
//                    Pr.set(j, Pr.get(j) + precipitation);
//                    j++;
//                }
//                i1 = i1 + step;
//            }
//        }
        List<Double> pr = Pr.stream()
                .map(value -> Math.round(value * 100.0) / 100.0)
                .collect(Collectors.toList());
        if (tms.size() != pr.size()) throw new Exception("数据错误！");
        HashMap<Object, Object> map = new HashMap<>();
        map.put("tms", tms);
        map.put("areaRain", pr);
        return map;
    }

    /**
     * 获取预测面雨量
     *
     * @param rscd
     * @param startTime
     * @param endTime
     * @param step
     * @return
     */
    public static HashMap<Object, Object> getpreAreaRainfall(String rscd, Date startTime, Date endTime, Integer step) throws Exception {
        List<String> rainStationCds = new ArrayList<>();
        List<Double> rainStationweight = new ArrayList<>();
        QueryWrapper<IotHLPoint> iotHLPointQueryWrapper = new QueryWrapper<>();
        iotHLPointQueryWrapper.eq("sttp", "3").eq("rscd", rscd);

        List<IotHLPoint> points = drpDataUtil.iIotHLPointService.list(iotHLPointQueryWrapper);
        for (int i = 0; i < points.size(); i++) {
            rainStationCds.add(points.get(i).getStcd());
            rainStationweight.add(points.get(i).getWeight());
        }
        // 时间序列
        List<String> tms = new ArrayList<>();
        // 面雨量数据
        List<Double> Pr = new ArrayList<>();
        for (int i = 0; i < rainStationCds.size(); i++) {
            int j = 0;
            // 查询雨量数据
            List<PptnRFuture> stationRains = drpDataUtil.pptnRFutureService.getpreRainfallData(DateUtil.format(startTime,"yyyy-MM-dd HH:mm:ss")
                    ,DateUtil.format(endTime,"yyyy-MM-dd HH:mm:ss"),step.toString(),rainStationCds.get(i));
            for (int i1 = 0; i1 < stationRains.size(); i1++) {
                Double precipitation = Double.valueOf(stationRains.get(i1).getDrp().toString()) * Double.valueOf(rainStationweight.get(i));
                if (i == 0) {
                    tms.add(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(stationRains.get(i1).getTm()));
                    Pr.add(precipitation);
                } else {
                    Pr.set(j, Pr.get(j) + precipitation);
                    j++;
                }
            }
        }
        List<Double> pr = Pr.stream()
                .map(value -> Math.round(value * 100.0) / 100.0)
                .collect(Collectors.toList());
        if (tms.size() != pr.size()) throw new Exception("数据错误！");
        HashMap<Object, Object> map = new HashMap<>();
        map.put("tms", tms);
        map.put("areaRain", pr);
        return map;
    }

    /**
     * 获取新安江模型计算结果统计信息
     *
     * @param xajResults
     * @return
     */
    public static Map<String, Object> getTotalDetail(List<XajResult> xajResults) {
        // 累计降雨量
        Double totalRain = xajResults.stream().mapToDouble(XajResult::getAreaRain).sum();
        XajResult xajResult = xajResults.stream().sorted(Comparator.comparingDouble(XajResult::getPreQ).reversed())
                .findFirst()
                .orElse(null);
        // 获取洪峰流量
        Double floodMax = xajResult.getPreQ();
        // 获取峰现时间
        String floodTime = xajResult.getTm();
        // 获取总洪量
        Double totalpreFlood = 0.0;
        for (int i = 0; i < xajResults.size(); i++) {
            if (i == xajResults.size() - 1) {
                totalpreFlood = totalpreFlood;
            } else {
                // 获取洪峰流量
                totalpreFlood = totalpreFlood + (xajResults.get(i).getPreQ() + xajResults.get(i + 1).getPreQ()) * 1800 / 10000;
            }
        }
        // 径流系数
//        Double runoffNum = 0.66;
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("totalRain", NumberUtil.round(totalRain, 1));
        resultMap.put("floodMax", NumberUtil.round(floodMax, 2));
        resultMap.put("totalpreFlood", NumberUtil.round(totalpreFlood, 2));
        resultMap.put("floodTime", floodTime);
//        resultMap.put("runoffNum", runoffNum);
        return resultMap;
    }

    /**
     * 调洪演算
     *
     * @param preQ 预报流量
     * @param tms  时间序列
     * @param rscd 水库编码
     * @return 出库流量
     */
    public static List<Double> jnRegulate(List<Double> preQ, List<String> tms, String rscd) throws Exception {
        QueryWrapper<Wrprsrwlstcparrl> wrprsrwlstcparrlQueryWrapper = new QueryWrapper<>();
        wrprsrwlstcparrlQueryWrapper.eq("rscd", rscd).orderByAsc("wl");
        List<Wrprsrwlstcparrl> wrprsrwlstcparrls = drpDataUtil.wrprsrwlstcparrlService.list(wrprsrwlstcparrlQueryWrapper);
        // 获取库容曲线序列
        List<Double> w = wrprsrwlstcparrls.stream().map(Wrprsrwlstcparrl::getStcp).collect(Collectors.toList());//库容量
        List<Double> z = wrprsrwlstcparrls.stream().map(Wrprsrwlstcparrl::getWl).collect(Collectors.toList());// 水位
        // 获取实测水位
        QueryWrapper<RsvrRLaste> rsvrRLasteQueryWrapper = new QueryWrapper<>();
//        rsvrRLasteQueryWrapper.eq("stcd", rscd).le("tm", DateUtil.parse(tms.get(0))).orderByDesc("tm")
//        rsvrRLasteQueryWrapper.eq("stcd", rscd).le("tm", new Date()).orderByDesc("tm")
//                .apply("date_trunc('hour', tm) = tm").last("LIMIT 1");
//        RsvrRLaste rsvrRLaste = drpDataUtil.rsvrRLasteService.getOne(rsvrRLasteQueryWrapper);
        String format = new SimpleDateFormat("yyyy-MM-dd HH:00:00").format(new Date());
        DateTime parse = DateUtil.parse(format);
        List<RsvrRLaste> rsvrRS = DataServiceUtil.getRsvrVoList(rscd, DateUtil.offsetDay(parse, -7), parse, 1);
        Double rzz = 0.0;
        if (rsvrRS.size()!=0){
            Optional<Double> lastNonNullRz = rsvrRS.stream()
                    .map(RsvrRLaste::getRz) // 提取 rz 字段的值
                    .filter(rz -> rz != null) // 过滤掉 rz 为 null 的值
                    .reduce((first, second) -> second); // 获取最后一个值
            rzz = lastNonNullRz.get();
        }
        // 获取当前水位对应的库容
        Double data = getData(z, w, rzz);
        // 计算新库容
        List<Double> n_w = new ArrayList<>();
        for (int i = 0; i < tms.size(); i++) {
            n_w.add(data + preQ.get(i)*3600/10000);
        }
        // 获取新水位
        List<Double> n_z = n_w.stream()
                .map(w1 -> getData(w, z, w1))
                .collect(Collectors.toList());
        return n_z;
    }

    /**
     * 调洪演算
     *
     * @param preQ 预报流量
     * @param tms  时间序列
     * @param rscd 水库编码
     * @return 出库流量
     */
    public static List<Double> regulate(List<Double> preQ, List<String> tms, String rscd) throws Exception {
        QueryWrapper<Wrprsrwlstcparrl> wrprsrwlstcparrlQueryWrapper = new QueryWrapper<>();
        wrprsrwlstcparrlQueryWrapper.eq("rscd", rscd).orderByAsc("wl");
        List<Wrprsrwlstcparrl> wrprsrwlstcparrls = drpDataUtil.wrprsrwlstcparrlService.list(wrprsrwlstcparrlQueryWrapper);
        // 获取库容曲线序列
        List<Double> w = wrprsrwlstcparrls.stream().map(Wrprsrwlstcparrl::getStcp).collect(Collectors.toList());//库容量
        List<Double> z = wrprsrwlstcparrls.stream().map(Wrprsrwlstcparrl::getWl).collect(Collectors.toList());// 水位
        // 获取实测水位
        QueryWrapper<RsvrRLaste> rsvrRLasteQueryWrapper = new QueryWrapper<>();
//        rsvrRLasteQueryWrapper.eq("stcd", rscd).le("tm", DateUtil.parse(tms.get(0))).orderByDesc("tm")
//        rsvrRLasteQueryWrapper.eq("stcd", rscd).le("tm", new Date()).orderByDesc("tm")
//                .apply("date_trunc('hour', tm) = tm").last("LIMIT 1");
//        RsvrRLaste rsvrRLaste = drpDataUtil.rsvrRLasteService.getOne(rsvrRLasteQueryWrapper);
        String format = new SimpleDateFormat("yyyy-MM-dd HH:00:00").format(new Date());
        DateTime parse = DateUtil.parse(format);
        List<RsvrRLaste> rsvrRS = DataServiceUtil.getRsvrVoList(rscd, DateUtil.offsetDay(parse, -7), parse, 1);
        Double rzz = 0.0;
        if (rsvrRS.size()!=0){
            Optional<Double> lastNonNullRz = rsvrRS.stream()
                    .map(RsvrRLaste::getRz) // 提取 rz 字段的值
                    .filter(rz -> rz != null) // 过滤掉 rz 为 null 的值
                    .reduce((first, second) -> second); // 获取最后一个值
            rzz = lastNonNullRz.get();
        }
        // 获取当前水位对应的库容
        Double data = getData(z, w, rzz);
        // 计算新库容
        List<Double> n_w = new ArrayList<>();
        for (int i = 0; i < tms.size(); i++) {
                n_w.add(data + preQ.get(i)*3600/10000);
        }
        // 获取新水位
        List<Double> n_z = n_w.stream()
                .map(w1 -> getData(w, z, w1))
                .collect(Collectors.toList());
        // 使用水位通过水位退水曲线计算出出库流量

        List<ZqrlB> zqrlBS = drpDataUtil.zqrlBService.lambdaQuery().eq(ZqrlB::getRscd, rscd).orderByAsc(ZqrlB::getZ).list();
        List<Double> rz = zqrlBS.stream().map(ZqrlB::getZ).collect(Collectors.toList());
        List<Double> outQ = zqrlBS.stream().map(ZqrlB::getQ).collect(Collectors.toList());
        // 获取警戒水位
        QueryWrapper<WrpRsrHych> wrpRsrHychQueryWrapper = new QueryWrapper<>();
        wrpRsrHychQueryWrapper.eq("rscd", rscd);
        Double mjflsscnwl = drpDataUtil.wrpRsrHychService.getOne(wrpRsrHychQueryWrapper).getMjflsscnwl();
        List<Double> n_outq = new ArrayList<>();
        for (int i = 0; i < n_z.size(); i++) {
            if (n_z.get(i) < mjflsscnwl) {
                n_outq.add(0.0);
            } else {
                Double q = getData(rz, outQ, n_z.get(i));
                n_outq.add(q);
            }
        }
        return n_outq;// 出库流量
    }

    /**
     * 水量平衡计算
     *
     * @param inq
     * @param outq
     * @param tms
     * @param rscd
     * @return 预报水位
     */
    public static List<Double> waterBalance(List<Double> inq, List<Double> outq, List<String> tms, String rscd) throws Exception {
        List<Wrprsrwlstcparrl> wrprsrwlstcparrls = drpDataUtil.wrprsrwlstcparrlService.lambdaQuery().eq(Wrprsrwlstcparrl::getRscd, rscd).orderByAsc(Wrprsrwlstcparrl::getWl).list();
        List<Double> pondage = wrprsrwlstcparrls.stream().map(Wrprsrwlstcparrl::getStcp).collect(Collectors.toList());
        List<Double> z = wrprsrwlstcparrls.stream().map(Wrprsrwlstcparrl::getWl).collect(Collectors.toList());
//        QueryWrapper<RsvrRLaste> rsvrRLasteQueryWrapper = new QueryWrapper<>();
//        rsvrRLasteQueryWrapper.eq("stcd", rscd).le("tm", new Date()).orderByDesc("tm")
//                .apply("date_trunc('hour', tm) = tm").last("LIMIT 1");
//        RsvrRLaste rsvrRLaste = drpDataUtil.rsvrRLasteService.getOne(rsvrRLasteQueryWrapper);
        // 查询当前水位值
        List<RsvrRLaste> rsvrRS = DataServiceUtil.getRsvrVoList(rscd, DateUtil.offsetDay(DateUtil.parse(tms.get(0)), -1), DateUtil.parse(tms.get(0)), 1);
        Double rz = 0.0;
        // 检查 rsvrRS 中的所有 rz 字段是否都为 null
        boolean allRzNull = rsvrRS.stream()
                .allMatch(rsvrRLaste -> rsvrRLaste.getRz() == null);
        if (rsvrRS.size()!=0&&!allRzNull){
            Optional<Double> lastNonNullRz = rsvrRS.stream()
                    .map(RsvrRLaste::getRz) // 提取 rz 字段的值
                    .filter(rz1 -> rz1 != null) // 过滤掉 rz 为 null 的值
                    .reduce((first, second) -> second); // 获取最后一个值
            rz = lastNonNullRz.get();
        }else {
            // 使用正常蓄水位
            // TODO: 2024/10/10  需要调整逻辑
            QueryWrapper<WrpRsrHych> wrpRsrHychQueryWrapper = new QueryWrapper<WrpRsrHych>().eq("rscd", rscd).last("limit 1");
            WrpRsrHych one = drpDataUtil.wrpRsrHychService.getOne(wrpRsrHychQueryWrapper);
            rz = one.getNrstlv();
        }
        // 计算出新的水库蓄水量
        Double finalRz = rz;
        List<Double> n_w = tms.stream()
                .map(tm -> {
                    return getData(z, pondage, finalRz) + inq.get(tms.indexOf(tm))*3600/10000 - outq.get(tms.indexOf(tm))*3600/10000;
                })
                .collect(Collectors.toList());
        // 获取新水位
        List<Double> n_z = n_w.stream()
                .map(w -> getData(pondage, z, w))
                .collect(Collectors.toList());
        return n_z;
    }

    /**
     * @param pondage 需要做匹配的值所在的序列
     * @param z       需要获取的序列值
     * @param w       需要做匹配的值
     * @return
     */
    public static Double getData(List<Double> pondage, List<Double> z, Double w) {
        int index = 0;
        Double minValue = Double.MAX_VALUE;
        // 找到最接近w的元素下标index
        for (int i = 0; i < pondage.size(); i++) {
            Double diff = Math.abs(pondage.get(i) - w);
            if (diff < minValue) {
                minValue = diff;
                index = i;
            }
        }
        int lowerIndex;
        int upperIndex;
        // 如果z等于水位集合中的某个元素
        if (w.equals(pondage.get(index))) {
            return z.get(index);
        } else if (w > pondage.get(index)) {
            lowerIndex = index;
            upperIndex = index + 1;
        } else if (w < pondage.get(0)) {
            return z.get(0);
        }  else if (w > pondage.get(pondage.size()-1)) {
            return z.get(pondage.size()-1);
        }else {
            lowerIndex = index - 1;
            upperIndex = index;
        }
        if (upperIndex == 18){
            upperIndex = 17;
        }
        // 如果z值在water_level集合中的两个相邻元素之间，加权平均处理
        Double distance1 = Math.abs(w - pondage.get(lowerIndex));
        Double distance2 = 0.0;
        try {
            distance2 = Math.abs(w - pondage.get(upperIndex));
        } catch (IndexOutOfBoundsException e) {
            e.printStackTrace();
            System.out.println("upperIndex:"+upperIndex);
        }
        Double weight1 = 1 - (distance1 / (distance1 + distance2));
        Double weight2 = 1 - (distance2 / (distance1 + distance2));
        Double newValue = weight1 * z.get(lowerIndex) + weight2 * z.get(upperIndex);
        return Math.round(newValue * 100.0) / 100.0;
    }

    public static List<Double> check(Integer step, List<String> tms, List<Double> q, String rscd) throws Exception {
        DateTime startTime = DateUtil.offsetHour(DateUtil.parse(tms.get(0)), -9);

        // 获取实测流量数据
//        QueryWrapper<RsvrRLaste> rsvrRLasteQueryWrapper = new QueryWrapper<>();
//        rsvrRLasteQueryWrapper.eq("stcd", rscd).ge("tm", startTime).le("tm", DateUtil.parse(tms.get(tms.size() - 1)))
//                .orderByAsc("tm");
//        List<RsvrRLaste> rsvrRLastes = drpDataUtil.rsvrRLasteService.list(rsvrRLasteQueryWrapper);
        List<RsvrRLaste> rsvrRLastes = DataServiceUtil.getRsvrVoList(rscd, startTime, DateUtil.parse(tms.get(tms.size() - 1)), 1);
        List<Double> realQ = new ArrayList<>();
        List<Double> preQ = new ArrayList<>();
        int j = 0;
        for (int i = 0; i < rsvrRLastes.size(); ) {
            realQ.add(rsvrRLastes.get(i).getInq());
            if (i <= 8) {
                preQ.add(rsvrRLastes.get(i).getInq());
                i = i + 1;
            } else {
                preQ.add(q.get(j));
                j++;
                i = i + step.intValue();
            }
        }
        String str = "reality,calculation" + "\n";
        for (int i = 0; i < realQ.size(); i++) {
            str += realQ.get(i) + "," + preQ.get(i) + "\n";
        }
        // 向beforeCheck.csv中写入数据
        // 文件输出路径
        String outputPath = System.getProperty("user.dir") + "/siyu-model/src/main/resources/csv/beforeCheck.csv";
        // 清空 beforeCheck.csv 文件
        try (FileWriter fw = new FileWriter(outputPath, false)) {
            fw.write("");
            fw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 将内容写入 beforeCheck.csv 文件
        try (FileWriter fw = new FileWriter(outputPath, true); BufferedWriter bw = new BufferedWriter(fw)) {
            bw.write(str); // 写入内容
            bw.flush(); // 刷新缓冲区
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 调用校正模型
        CheckModel.calculatecheck();
        // 获取校正数据
        String resultfilePath = System.getProperty("user.dir") + "/siyu-model/src/main/resources/csv/afterCheck.csv";
        List<Double> checkZ = new ArrayList<>();
        try (BufferedReader br = new BufferedReader(new FileReader(resultfilePath))) {
            String line;
            while ((line = br.readLine()) != null) {
                double number = Double.parseDouble(line);

                DecimalFormat df = new DecimalFormat("#.00");
                String formattedNumber = df.format(number);
                double formattedResult = Double.parseDouble(formattedNumber);
                checkZ.add(formattedResult);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return checkZ;
    }

}
